package org.haredot.proxy;

import org.haredot.annotation.Transactional;
import org.haredot.exception.DaoAccessException;
import org.haredot.utils.ClassUtils;
import org.haredot.utils.DBUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.List;
import java.util.Objects;

public class TranscationProxy {

    @SuppressWarnings("unchecked")
    public static <T> T getProxy(Object target, Class<T> clazz) {
        return (T) getProxy(target);
    }
    /**
     * 根据传入的 目标对象，返回 代理对象
     *
     * @param target
     * @return
     */
    public static Object getProxy(Object target) {
        /**
         *  newProxyInstance(classLoader,  interfaces,  invocationHandler)
         *      classLoader : 类加载器
         *      interfaces : 目标对象实现的 接口列表
         *      invocationHandler : 切面处理器
         *             proxy : 代理对象， 一般不要使用
         *             method : 代理对象中  执行的方法
         *             args :  目标对象中 执行方法的参数列表
         *
         */
        Class<?> aClassTarget = target.getClass();

        return Proxy.newProxyInstance(aClassTarget.getClassLoader(),
                aClassTarget.getInterfaces(), (proxy, method, args) -> {
                    // 编写 切面代码
                    Connection connection = DBUtils.getConnection();
                    // 开启事务
                    connection.setAutoCommit(false);

                    try {
                        // 目标 对象开始 执行 业务
                        Object ret = method.invoke(target, args);
                        // 提交 事务
                        connection.commit();
                        // 返回 目标对象 执行的结果
                        return  ret ;
                    } catch (Throwable e) {
                        // InvocationTargetException 异常类 可以通过 getTargetException()方法 获取 抛出的真实异常
                        if (e instanceof InvocationTargetException ex) {
                            //  获取 方法上的 @Transactional注解，如果方法中没有，则获取类上的该注解
                            Transactional annotation = null ;
                            if ((annotation =method.getAnnotation(Transactional.class))!=null || (annotation = aClassTarget.getAnnotation(Transactional.class))!=null ) {
                                Class<? extends Throwable> aClass = annotation.rollbackFor();
                                // 获取 抛出异常的所有父类
                                List<Class<?>> allSuperClasses = ClassUtils.getAllSuperClasses(ex.getTargetException().getClass());
                                // 如果 抛出的异常是指定的异常，获取 是 指定异常的 子类
                                if (aClass == ex.getTargetException().getClass() ||  allSuperClasses.contains(aClass)) {
                                    // 事务进行 回滚
                                    connection.rollback();
                                }
                            }
                            // 无论是否有 @Transactional注解，均将抛出的异常继续向上层调用者 抛出
                            throw new DaoAccessException(ex.getTargetException());
                        }
                        // 如果不是代理对象产生的异常、则直接抛出
                        throw new DaoAccessException(e);
                    }finally {
                        DBUtils.closeConnection();
                    }
                }) ;
    }
}
